//
// Created by Dell on 2021/5/31.
//
#include "scene.h"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/ext.hpp"
#include "glm/detail/_noise.hpp"

class Section502 : public Scene {
private:
    // 将数据从cpu放到 gpu
    GLuint vbo, ebo;
    GLuint program;
    GLint positionLocation, modelMatrixLocation, viewMatrixLocation, projectMatrixLocation, colorLocation;
    GLint textureCoordLocation, textureLocation;
    GLint texture;
    // 不初始化就是单位矩阵
    glm::mat4 modelMatrix, viewMatrix, projectMatrix;


public:

    void init() override {
        //顶点数据，每个顶点包含10个float用于描述数据，坐标（4）|颜色（4）|纹理坐标（2）
        float data[] = {
                -0.2f, -0.2f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
                0.2f, -0.2f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
                0.0f, 0.2f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f
        };
        //显存申请一个顶点缓冲对象VBO，取得id
        glGenBuffers(1, &vbo);
        //gl绑定当前操作的顶点缓冲对象
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        //设置当前的顶点缓冲对象数据，总数据长度，数据，
        //GL_STATIC_DRAW 表示数据不会变化，可以多次使用
        //GL_STREAM_DRAW 表示每帧数据都会变化，一次修改一次使用
        //GL_DYNAMIC_DRAW 表示数据会频繁变化，多次修改，多次使用
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 30, data, GL_STATIC_DRAW);
        //接触当前绑定的缓冲对象
        glBindBuffer(GL_ARRAY_BUFFER, 0);// 设置当前buffer为0 即解绑

        //显存申请一个索引缓冲对象，用于指定绘制的点的索引，多个三角形可以通过这个来减少重复点的位置声明
        unsigned short indexes[] = {0, 1, 2};
        glGenBuffers(1, &ebo);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * 3, indexes, GL_STATIC_DRAW);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

        int fileSize = 0;
        //加载顶点着色器代码
        unsigned char *shaderCode = LoadFileContent(aAssetManager, "502.vsh", fileSize);
        //通过显卡进行编译，取得着色器的id
        GLuint vsShader = CompileShader(GL_VERTEX_SHADER, (char *) shaderCode);
        //着色器若编译通过，代码则已经编译后存放与显存中，这里可以直接删除加载的着色器脚本内容
        delete shaderCode;
        //同样的步骤将片段着色器也加载到显存中
        shaderCode = LoadFileContent(aAssetManager, "502.fsh", fileSize);
        GLint fsShader = CompileShader(GL_FRAGMENT_SHADER, (char *) shaderCode);
        //创建渲染程序
        program = CreateProgram(vsShader, fsShader);
        //gl删除已经加载的两个着色器
        glDeleteShader(vsShader);
        glDeleteShader(fsShader);

        //根据名称获取顶点着色器中要输入的属性id
        //三个变换矩阵
        modelMatrixLocation = glGetUniformLocation(program, "ModelMatrix");
        viewMatrixLocation = glGetUniformLocation(program, "ViewMatrix");
        projectMatrixLocation = glGetUniformLocation(program, "ProjectionMatrix");
        //顶点位置
        positionLocation = glGetAttribLocation(program, "position");
        //顶点颜色
        colorLocation = glGetAttribLocation(program, "color");
        //顶点对应的2d纹理坐标
        textureCoordLocation = glGetAttribLocation(program, "texcoord");

        //片段着色器 2d纹理
        textureLocation = glGetUniformLocation(program, "U_texture");

        //设置相机得到视图变换矩阵
        viewMatrix = glm::lookAt(
                glm::vec3(0.0f, 0.0f, 1.0f),//相机位置
                glm::vec3(0.0f, 0.0f, 0.0f),//观察位置
                glm::vec3(0.0f, 1.0f, 0.0f)//相机正上方朝向
        );


        //设置模型变换矩阵
        //通过gl math库初始化一个4x4矩阵
        glm::mat4 model;
        //通过glm平移矩阵，z轴方向偏移-0.6f
        modelMatrix = glm::translate(model, glm::vec3(0.0f, 0.0f, -0.0f));
//    //模型缩放,y方向放大2倍
//    modelMatrix = glm::scale(
//            modelMatrix,
//            glm::vec3(1.0f, 2.0f, 1.0f)
//    );
//    //模型绕z轴旋转180度,glm::radians用于将角度值转弧度值
//    modelMatrix = glm::rotate(
//            modelMatrix,
//            glm::radians(180.0f),
//            glm::vec3(0.0f, 0.0f, 1.0f)
//    );

        //创建纹理
        texture = CreateTextureFromBMP(aAssetManager, "test.bmp");
    }

    void setViewPortSize(float width, float height) override {
        //设置视窗尺寸
//        glViewport(0, 0, width, height);
        //设置投影矩阵，决定了投影到视窗的3d空间范围，参数分别为，上下视角，宽高比、最近看到的距离、最远看到的距离
        //最近可观察相机前方0.0单位的位置，可观察最远距离1000单位的位置
        //宽高比参照显示的窗口宽高去设置，否则投影画面会强制缩放到viewport的大小导致画面拉伸
        projectMatrix = glm::perspective(45.0f, width / height, 0.0f, 1000.0f);
    }

    void draw() override {
        //设置清屏颜色
        glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        //清除颜色缓冲和深度缓冲
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        //启用深度测试
//        glEnable(GL_DEPTH_TEST);
        //载入渲染程序
        glUseProgram(program);

        //设置顶点着色器变幻矩阵
        glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, glm::value_ptr(modelMatrix));
        glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, glm::value_ptr(viewMatrix));
        glUniformMatrix4fv(projectMatrixLocation, 1, GL_FALSE, glm::value_ptr(projectMatrix));

        //设置片段着色器纹理
        //gl可以加载多个纹理,每个纹理可以对应一个纹理下标
        GLint index = 0;
        //绑定到片段着色器纹理地址
        glUniform1i(textureLocation, index);
        //激活纹理
        glActiveTexture(GL_TEXTURE0 + index);
        //绑定纹理
        glBindTexture(GL_TEXTURE_2D, texture);

        //绑定顶点缓冲对象
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        //启用顶点着色器输入值
        glEnableVertexAttribArray(positionLocation);
        glEnableVertexAttribArray(textureCoordLocation);
        glEnableVertexAttribArray(colorLocation);
        //将顶点缓冲对象的数据设置给顶点着色器的输入值（in）
        // 参数说明： GL_FALSE 表示是否需要将数据映射到0-1，这里本来是浮点，不需要；
        //0 表示vbo 中数据的起始位置
        glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 10, 0);
        glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 10,
                              (void *) (sizeof(float) * 4));
        glVertexAttribPointer(textureCoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 10,
                              (void *) (sizeof(float) * 8));

//    glDrawArrays(GL_TRIANGLES, 0 , 3);
        // 绑定索引缓冲对象
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
        //绘制元素，三角形
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0);

        //解除索引缓冲对象与顶点缓冲对象的绑定
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindTexture(GL_TEXTURE_2D, 0);
        //解除渲染程序
        glUseProgram(0);
    }

    explicit Section502(AAssetManager *aAssetManager) : Scene(aAssetManager) {

    }

    ~Section502() = default;
};

